package com.bdk.middle.common.util;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;

/**
 * Map 转换工具
 *
 * @author sp42 frank@ajaxjs.com
 *
 */
public class MapTool {
    private static Logger logger = LoggerFactory.getLogger(MapTool.class.getName());

    // --------------------------------------------------------------------------------------------------
    // -----------------------------------------------Map转换---------------------------------------------
    // --------------------------------------------------------------------------------------------------

    /**
     * Map 转换为 String
     *
     * @param map Map 结构，Key 必须为 String 类型
     * @param div 分隔符
     * @param fn  对 Value 的处理函数，返回类型 T
     * @return Map 序列化字符串
     */
    public static <T> String join(Map<String, T> map, String div, Function<T, String> fn) {
        String[] pairs = new String[map.size()];

        int i = 0;

        for (String key : map.keySet())
            pairs[i++] = key + "=" + fn.apply(map.get(key));

        return String.join(div, pairs);
    }

    public static <T> String join(Map<String, T> map, Function<T, String> fn) {
        return join(map, "&", fn);
    }

    public static <T> String join(Map<String, T> map, String div) {
        return join(map, div, v -> v == null ? null : v.toString());
    }

    public static <T> String join(Map<String, T> map) {
        return join(map, "&");
    }

    /**
     * String[] 转换为 Map
     *
     * @param pairs 结对的字符串数组，包含 = 字符分隔 key 和 value
     * @param fn    对 Value 的处理函数，返回类型 Object
     * @return Map 对象
     */
    public static Map<String, Object> toMap(String[] pairs, Function<String, Object> fn) {
        if (CommonUtil.isNull(pairs))
            return null;

        Map<String, Object> map = new HashMap<>();

        for (String pair : pairs) {
            if (!pair.contains("="))
                throw new IllegalArgumentException("没有 = 不能转化为 map");

            String[] column = pair.split("=");

            if (column.length >= 2)
                map.put(column[0], fn == null ? column[1] : fn.apply(column[1]));
            else
                map.put(column[0], "");// 没有 等号后面的，那设为空字符串
        }

        return map;
    }

    /**
     * String[] 转换为 Map，key 与 value 分别一个数组
     *
     * @param columns 结对的键数组
     * @param values  结对的值数组
     * @param fn      对 Value 的处理函数，返回类型 Object
     * @return Map 对象
     */
    public static Map<String, Object> toMap(String[] columns, String[] values, Function<String, Object> fn) {
        if (CommonUtil.isNull(columns))
            return null;

        if (columns.length != values.length)
            throw new UnsupportedOperationException("两个数组 size 不一样");

        Map<String, Object> map = new HashMap<>();

        int i = 0;
        for (String column : columns)
            map.put(column, fn.apply(values[i++]));

        return map;
    }

    /**
     * 判断 map 非空，然后根据 key 获取 value，若 value 非空则作为参数传入函数接口 s
     *
     * @param map 输入的map
     * @param key map的键
     * @param s   如果过非空，那么接着要做什么？在这个回调函数中处理。传入的参数就是map.get(key)的值
     */
    public static <T> void getValue(Map<String, T> map, String key, Consumer<T> s) {
        if (map != null) {
            T value = map.get(key);
            if (value != null)
                s.accept(value);
        }
    }

    /**
     * 万能 Map 转换器，为了泛型的转换而设的一个方法，怎么转换在 fn 中处理
     *
     * @param map 原始 Map，key 必须为 String 类型
     * @param fn  转换函数
     * @return 转换后的 map
     */
    public static <T, K> Map<String, T> as(Map<String, K> map, Function<K, T> fn) {
        Map<String, T> _map = new HashMap<>();
        map.forEach((k, v) -> _map.put(k, v == null ? null : fn.apply(v)));

        return _map;
    }

    public static Map<String, Object> as(Map<String, String[]> map) {
        return as(map, arr -> MappingValue.toJavaValue(arr[0]));
    }

    // --------------------------------------------------------------------------------------------------
    // -----------------------------------------------Bean-----------------------------------------------
    // --------------------------------------------------------------------------------------------------

    @FunctionalInterface
    public static interface EachFieldArg {
        public void item(String key, Object value, PropertyDescriptor property);
    }

    /**
     * 遍历一个 Java Bean
     *
     * @param bean Java Bean
     * @param fn   执行的任务，参数有 key, value, property
     */
    public static void eachField(Object bean, EachFieldArg fn) {
        try {
            BeanInfo beanInfo = Introspector.getBeanInfo(bean.getClass());

            for (PropertyDescriptor property : beanInfo.getPropertyDescriptors()) {
                String key = property.getName();

                // 得到 property 对应的 getter 方法
                Method getter = property.getReadMethod();
                Object value = getter.invoke(bean);

                fn.item(key, value, property);
            }
        } catch (IntrospectionException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
            logger.debug(e.getMessage());
        }
    }

    /**
     * Map 转为 Bean
     *
     * @param map         原始数据
     * @param clz         实体 bean 的类
     * @param isTransform 是否尝试转换值
     * @return 实体 bean 对象
     */
    public static <T> T map2Bean(Map<String, ?> map, Class<T> clz, boolean isTransform) {
        T bean = ReflectUtil.newInstance(clz);

        eachField(bean, (key, v, property) -> {

            try {
                if (map.containsKey(key)) {
                    Object value = map.get(key);

                    // null 是不会传入 bean 的
                    if (value != null) {
                        Class<?> t = property.getPropertyType(); // Bean 值的类型，这是期望传入的类型，也就 setter 参数的类型

                        if (isTransform && t != value.getClass()) { // 类型相同，直接传入；类型不相同，开始转换
                            value = MappingValue.objectCast(value, t);
                        }

//						logger.debug("t:" + t);
//						logger.debug("v:" + value + " type: " + value.getClass());

                        try {
                            property.getWriteMethod().invoke(bean, value);
                        }catch (Exception e){
                            System.out.println(t);
                            System.out.println(value.getClass());
                            System.out.println(t != value.getClass());
                            e.printStackTrace();

                        }
                    }
                }

                // 子对象
                for (String mKey : map.keySet()) {
                    if (mKey.contains(key + '_')) {
                        Method getter = property.getReadMethod(), setter = property.getWriteMethod();// 得到对应的 setter 方法

                        Object subBean = getter.invoke(bean);
                        String subBeanKey = mKey.replaceAll(key + '_', "");

                        if (subBean != null) {// 已有子 bean
                            if (map.get(mKey) != null) // null 值不用处理
                                ReflectUtil.setProperty(subBean, subBeanKey, map.get(mKey));
                        } else { // map2bean
                            Map<String, Object> subMap = new HashMap<>();
                            subMap.put(subBeanKey, map.get(mKey));
                            subBean = map2Bean(subMap, setter.getParameterTypes()[0], isTransform);
                            setter.invoke(bean, subBean); // 保存新建的 bean
                        }
                    }
                }
            } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
                logger.debug(e.getMessage());
            }
        });

        return bean;
    }

    /**
     * map 转实体
     *
     * @param map 原始数据
     * @param clz 实体 bean 的类
     * @return 实体 bean 对象
     */
    public static <T> T map2Bean(Map<String, ?> map, Class<T> clz) {
        return map2Bean(map, clz, false);
    }

    /**
     * Bean 转为 Map
     *
     * @param bean 实体 bean 对象
     * @return Map 对象
     */
    public static <T> Map<String, Object> bean2Map(T bean) {
        Map<String, Object> map = new HashMap<>();

        eachField(bean, (k, v, property) -> {
            if (!k.equals("class")) // 过滤 class 属性
                map.put(k, v);
        });

        return map;
    }
}
